use crate::io::Config;
use crate::server::*;
use manager::ServerManager;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::{Arc, Mutex};

/// A structure that manages the average value computation for a set of servers.
pub struct AverageManager {
    server_manager: Arc<Mutex<ServerManager>>, // The server manager handling server instances.
    actual_average: f64,                       // The current computed average value.
    current_average: f64,                      // The current average value during computation.
    rounds: usize,                             // The number of rounds completed.
    config: Config, // The configuration settings for the average manager.
}

impl AverageManager {
    /// Creates a new instance of `AverageManager` with the given configuration.
    ///
    /// # Arguments
    ///
    /// * `config` - Configuration settings for the average manager.
    ///
    /// # Returns
    ///
    /// A new instance of `AverageManager`.
    pub fn new(config: Config) -> Self {
        AverageManager {
            server_manager: Arc::new(Mutex::new(ServerManager::new())),
            actual_average: 0.0,
            current_average: 0.0,
            rounds: 0,
            config,
        }
    }

    /// Initializes the AverageManager by setting up servers and the adjacency matrix.
    pub fn init(&mut self) {
        self.setup_servers(); // Setup server instances.
        self.setup_adjacency_matrix(); // Setup adjacency matrix for server connections.
        self.actual_average = self.calculate_average(); // Calculate the initial average.
    }

    /// Sets up server instances and adds them to the server manager.
    fn setup_servers(&mut self) {
        let mut server_manager = self.server_manager.lock().unwrap();
        for i in 0..self.config.node_num {
            let server = Server::new(
                IpAddr::V4(Ipv4Addr::new(127, 0, 0, i as u8)), // Assigning unique IP addresses.
                i as f64 * 10.0,                               // Initial value for the server.
                self.config.k_value,
            );
            server_manager.add_server(server); // Add the server to the manager.
        }
    }

    /// Sets up the adjacency matrix to define the connection relationships between servers.
    fn setup_adjacency_matrix(&self) {
        let server_manager = self.server_manager.lock().unwrap();
        if let Some(_input_file) = &self.config.input_file {
            // TODO
            // Implement reading adjacency matrix from file.
            // Placeholder for reading logic
            // let adjacency_matrix = read_adjacency_matrix_from_file(input_file);
            // For now, let's just panic as the reading logic is not implemented.
            panic!("Reading adjacency matrix from file is not implemented.");
        } else {
            // Build a fully connected graph based on self.config.node_num
            let node_num = self.config.node_num;
            let mut adjacency_matrix = vec![vec![0; node_num]; node_num];
            for i in 0..node_num {
                for j in 0..node_num {
                    if i != j {
                        adjacency_matrix[i][j] = 1;
                    }
                }
            }

            // Add neighbors based on the fully connected adjacency matrix
            for i in 0..node_num {
                for j in 0..node_num {
                    if adjacency_matrix[i][j] == 1 {
                        server_manager.add_neighbor(i, j);
                    }
                }
            }
        }
    }

    /// Executes the average computation process until completion.
    ///
    /// This method continues to perform gossip communication among servers,
    /// update the average value, and display the current state until all servers
    /// are finished computing.
    pub fn exec(&mut self) {
        while !self.finished() {
            self.gossip_servers(); // Perform gossip communication among servers.
            self.current_average = self.calculate_average(); // Update the actual average.
            if self.config.logger {
                self.show(); // Display current state of the average manager.
            }
            self.rounds += 1; // Increment the round count.
        }
    }

    /// Facilitates gossip communication between servers by spawning threads.
    fn gossip_servers(&self) {
        let server_manager = Arc::clone(&self.server_manager);
        server_manager
            .lock()
            .unwrap()
            .apply_to_all_servers(|s| s.gossip());
    }

    /// Displays the current average value and server information.
    ///
    /// This method prints out the actual average, current average, number of rounds,
    /// and details of each server to the console.
    pub fn show(&self) {
        println!("#########################################################");
        println!("AverageManager Information:");
        println!("Actual Average: {}", self.actual_average);
        println!("Current Average: {}", self.current_average);
        println!("Rounds: {}", self.rounds);

        let server_manager = self.server_manager.lock().unwrap();
        println!("Servers Information:");
        for server in server_manager.servers.values() {
            println!("{:?}", server.lock().unwrap());
        }
        println!("#########################################################");
    }

    /// Returns the result of the average computation.
    ///
    /// # Returns
    ///
    /// A tuple containing the difference between the actual average and the current average,
    /// and the number of rounds completed.
    pub fn result(&self) -> (f64, usize) {
        (
            (self.actual_average - self.current_average).abs(),
            self.rounds,
        )
    }

    /// Checks if the average computation is finished.
    fn finished(&self) -> bool {
        self.server_manager
            .lock()
            .unwrap()
            .servers
            .values()
            .all(|server| server.lock().unwrap().finished()) // All servers must be finished.
    }

    /// Calculates the current average value based on the server values.
    fn calculate_average(&self) -> f64 {
        let server_manager = self.server_manager.lock().unwrap();
        let total: f64 = server_manager
            .servers
            .values()
            .map(|server| server.lock().unwrap().number())
            .sum(); // Get each server's number.
        total / self.config.node_num as f64 // Average = Total sum / Number of servers.
    }
}
